using System;
using System.Runtime.InteropServices;
using GLWrapper;
using GameBaseCode;

public class ObjMesh
{
    public ObjMesh(string fileName)
    {
        ObjMeshLoader.Load(this, fileName);
    }

    public ObjMesh(Vector3[] verts, Vector3[] normals, Vector2[] coords, ushort[] indices)
    {
        this.normalArray = normals;
        this.vertexArray = verts;
        this.texCoordArray = coords;
        this.indexArray = indices;
    }

    public ObjVertex[] Vertices
    {
        get { return vertices; }
        set { vertices = value; }
    }
    ObjVertex[] vertices;

    public ObjTriangle[] Triangles
    {
        get { return triangles; }
        set { triangles = value; }
    }
    ObjTriangle[] triangles;

    public ObjQuad[] Quads
    {
        get { return quads; }
        set { quads = value; }
    }
    ObjQuad[] quads;

    public Vector3[] normalArray;
    public Vector3[] vertexArray;
    public Vector2[] texCoordArray;
    public ushort[] indexArray;
    public uint texHandle;

    //UInt32 verticesBufferId;
    //int trianglesBufferId;
    //int quadsBufferId;

    //public void Prepare()
    //{
    //    if (verticesBufferId == 0)
    //    {
    //        unsafe
    //        {
    //            fixed (UInt32* ptr = &verticesBufferId)
    //            GL.glGenBuffers(1, ptr);
    //            GL.glBindBuffer(BufferTarget.ArrayBuffer, verticesBufferId);
    //            GL.glBufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * Marshal.SizeOf(typeof(ObjVertex))), vertices, BufferUsageHint.StaticDraw);
    //        }
    //    }

    //    if (trianglesBufferId == 0)
    //    {
    //        GL.glGenBuffers(1, out trianglesBufferId);
    //        GL.glBindBuffer(BufferTarget.ElementArrayBuffer, trianglesBufferId);
    //        GL.glBufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(triangles.Length * Marshal.SizeOf(typeof(ObjTriangle))), triangles, BufferUsageHint.StaticDraw);
    //    }

    //    if (quadsBufferId == 0)
    //    {
    //        GL.glGenBuffers(1, out quadsBufferId);
    //        GL.glBindBuffer(BufferTarget.ElementArrayBuffer, quadsBufferId);
    //        GL.glBufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(quads.Length * Marshal.SizeOf(typeof(ObjQuad))), quads, BufferUsageHint.StaticDraw);
    //    }
    //}

    //public void RenderVBO()
    //{
    //    Prepare();

    //    GL.PushClientAttrib(ClientAttribMask.ClientVertexArrayBit);
    //    GL.EnableClientState(EnableCap.VertexArray);
    //    GL.BindBuffer(BufferTarget.ArrayBuffer, verticesBufferId);
    //    GL.InterleavedArrays(InterleavedArrayFormat.T2fN3fV3f, Marshal.SizeOf(typeof(ObjVertex)), IntPtr.Zero);

    //    GL.BindBuffer(BufferTarget.ElementArrayBuffer, trianglesBufferId);
    //    GL.DrawElements(BeginMode.Triangles, triangles.Length * 3, DrawElementsType.UnsignedInt, IntPtr.Zero);

    //    if (quads.Length > 0)
    //    {
    //        GL.BindBuffer(BufferTarget.ElementArrayBuffer, quadsBufferId);
    //        GL.DrawElements(BeginMode.Quads, quads.Length * 4, DrawElementsType.UnsignedInt, IntPtr.Zero);
    //    }

    //    GL.PopClientAttrib();
    //}

    private void RenderVA()
    {
        //GL.glEnableClientState(GL.GL_NORMAL_ARRAY);
        //GL.glEnableClientState(GL.GL_COLOR_ARRAY);
        GL.glEnableClientState(GL.GL_VERTEX_ARRAY);
        GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
        
        unsafe
        {

            //fixed (float* ptr = &normalArray[0].x)
            //    GL.glNormalPointer(GL.GL_FLOAT, 0, ptr);

            fixed (float* ptr = &texCoordArray[0].x)
                GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, ptr);

            fixed (float* ptr = &vertexArray[0].x)
                GL.glVertexPointer(3, GL.GL_FLOAT, 0, ptr);

            fixed (ushort* ptr = &indexArray[0])
                GL.glDrawElements(GL.GL_TRIANGLES, indexArray.Length, GL.GL_UNSIGNED_SHORT, ptr);
        }

        //GL.glDrawArrays(GL.GL_TRIANGLES, 0, triangles.Length*3);

        GL.glDisableClientState(GL.GL_VERTEX_ARRAY);  // disable vertex arrays
        //GL.glDisableClientState(GL.GL_COLOR_ARRAY);
        //GL.glDisableClientState(GL.GL_NORMAL_ARRAY);
        GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY);
    }

    private void RenderImmidiate()
    {
        //GL.glBegin(GL.GL_TRIANGLES);
        //for (int i = 0; i < triangles.Length; i++)
        //{
        //    int v1 = triangles[i].Index0;
        //    int v2 = triangles[i].Index1;
        //    int v3 = triangles[i].Index2;

        //    Vector3 ve1 = Vertices[v1].Vertex;
        //    Vector3 ve2 = Vertices[v2].Vertex;
        //    Vector3 ve3 = Vertices[v3].Vertex;

        //    GL.glTexCoord2f(Vertices[v1].TexCoord.x, Vertices[v1].TexCoord.y);
        //    GL.glNormal3f(Vertices[v1].Normal.x, Vertices[v1].Normal.y, Vertices[v1].Normal.z);
        //    GL.glVertex3f(ve1.x, ve1.y, ve1.z);

        //    GL.glTexCoord2f(Vertices[v2].TexCoord.x, Vertices[v2].TexCoord.y);
        //    GL.glNormal3f(Vertices[v2].Normal.x, Vertices[v2].Normal.y, Vertices[v2].Normal.z);
        //    GL.glVertex3f(ve2.x, ve2.y, ve2.z);

        //    GL.glTexCoord2f(Vertices[v3].TexCoord.x, Vertices[v3].TexCoord.y);
        //    GL.glNormal3f(Vertices[v3].Normal.x, Vertices[v3].Normal.y, Vertices[v3].Normal.z);
        //    GL.glVertex3f(ve3.x, ve3.y, ve3.z);
        //}
        //GL.glEnd();

        //GL.glBegin(GL.GL_QUADS);
        //for (int i = 0; i < quads.Length; i++)
        //{
        //    Vector3 ve1 = Vertices[quads[i].Index0].Vertex;
        //    Vector3 ve2 = Vertices[quads[i].Index1].Vertex;
        //    Vector3 ve3 = Vertices[quads[i].Index2].Vertex;
        //    Vector3 ve4 = Vertices[quads[i].Index3].Vertex;

        //    GL.glTexCoord2f(Vertices[quads[i].Index0].TexCoord.x, Vertices[quads[i].Index0].TexCoord.y);
        //    GL.glNormal3f(Vertices[quads[i].Index0].Normal.x, Vertices[quads[i].Index0].Normal.y, Vertices[quads[i].Index0].Normal.z);
        //    GL.glVertex3f(ve1.x, ve1.y, ve1.z);

        //    GL.glTexCoord2f(Vertices[quads[i].Index1].TexCoord.x, Vertices[quads[i].Index1].TexCoord.y);
        //    GL.glNormal3f(Vertices[quads[i].Index1].Normal.x, Vertices[quads[i].Index1].Normal.y, Vertices[quads[i].Index1].Normal.z);
        //    GL.glVertex3f(ve2.x, ve2.y, ve2.z);

        //    GL.glTexCoord2f(Vertices[quads[i].Index2].TexCoord.x, Vertices[quads[i].Index2].TexCoord.y);
        //    GL.glNormal3f(Vertices[quads[i].Index2].Normal.x, Vertices[quads[i].Index2].Normal.y, Vertices[quads[i].Index2].Normal.z);
        //    GL.glVertex3f(ve3.x, ve3.y, ve3.z);

        //    GL.glTexCoord2f(Vertices[quads[i].Index3].TexCoord.x, Vertices[quads[i].Index3].TexCoord.y);
        //    GL.glNormal3f(Vertices[quads[i].Index3].Normal.x, Vertices[quads[i].Index3].Normal.y, Vertices[quads[i].Index3].Normal.z);
        //    GL.glVertex3f(ve4.x, ve4.y, ve4.z);
        //}
        //GL.glEnd();
    }

        public void Render()
        {
            GL.glBindTexture(GL.GL_TEXTURE_2D, texHandle);

            RenderVA();
        }

        public void RenderTexcoords()
        {
                GL.glBegin(GL.GL_TRIANGLES);
                for (int i = 0; i < triangles.Length; i++)
                {       
                    Vector3 ve1 = Vertices[triangles[i].Index0].Vertex;
                    Vector3 ve2 = Vertices[triangles[i].Index1].Vertex;
                    Vector3 ve3 = Vertices[triangles[i].Index2].Vertex;

                    GL.glColor3f(Vertices[triangles[i].Index0].TexCoord.x, 0.0f, Vertices[triangles[i].Index0].TexCoord.y);
                    //GL.glNormal3f(Vertices[v1].Normal.x, Vertices[v1].Normal.y, Vertices[v1].Normal.z);
                    GL.glVertex3f(ve1.x, ve1.y, ve1.z);

                    GL.glColor3f(Vertices[triangles[i].Index1].TexCoord.x, 0.0f, Vertices[triangles[i].Index1].TexCoord.y);
                    //GL.glNormal3f(Vertices[v2].Normal.x, Vertices[v2].Normal.y, Vertices[v2].Normal.z);
                    GL.glVertex3f(ve2.x, ve2.y, ve2.z);

                    GL.glColor3f(Vertices[triangles[i].Index2].TexCoord.x, 0.0f, Vertices[triangles[i].Index2].TexCoord.y);
                    //GL.glNormal3f(Vertices[v3].Normal.x, Vertices[v3].Normal.y, Vertices[v3].Normal.z);
                    GL.glVertex3f(ve3.x, ve3.y, ve3.z);  
                }
                GL.glEnd();

                GL.glBegin(GL.GL_QUADS);
                for (int i = 0; i < quads.Length; i++)
                {
                    Vector3 ve1 = Vertices[quads[i].Index0].Vertex;
                    Vector3 ve2 = Vertices[quads[i].Index1].Vertex;
                    Vector3 ve3 = Vertices[quads[i].Index2].Vertex;
                    Vector3 ve4 = Vertices[quads[i].Index3].Vertex;

                    GL.glColor3f(Vertices[quads[i].Index0].TexCoord.x, 0.0f, Vertices[quads[i].Index0].TexCoord.y);
                    //GL.glNormal3f(Vertices[quads[i].Index0].Normal.x, Vertices[quads[i].Index0].Normal.y, Vertices[quads[i].Index0].Normal.z);
                    GL.glVertex3f(ve1.x, ve1.y, ve1.z);

                    GL.glColor3f(Vertices[quads[i].Index1].TexCoord.x, 0.0f, Vertices[quads[i].Index1].TexCoord.y);
                    //GL.glNormal3f(Vertices[quads[i].Index1].Normal.x, Vertices[quads[i].Index1].Normal.y, Vertices[quads[i].Index1].Normal.z);
                    GL.glVertex3f(ve2.x, ve2.y, ve2.z);

                    GL.glColor3f(Vertices[quads[i].Index2].TexCoord.x, 0.0f, Vertices[quads[i].Index2].TexCoord.y);
                    //GL.glNormal3f(Vertices[quads[i].Index2].Normal.x, Vertices[quads[i].Index2].Normal.y, Vertices[quads[i].Index2].Normal.z);
                    GL.glVertex3f(ve3.x, ve3.y, ve3.z);

                    GL.glColor3f(Vertices[quads[i].Index3].TexCoord.x, 0.0f, Vertices[quads[i].Index3].TexCoord.y);
                    //GL.glNormal3f(Vertices[quads[i].Index3].Normal.x, Vertices[quads[i].Index3].Normal.y, Vertices[quads[i].Index3].Normal.z);
                    GL.glVertex3f(ve4.x, ve4.y, ve4.z);
                }
                GL.glEnd();
        }

        //public void RenderNormals()
        //{
        //    GL.glColor3f(0.0f, 1.0f, 0.0f);
        //    GL.glBegin(GL.GL_LINES);
        //    for (int i = 0; i < triangles.Length; i++)
        //    {
        //        int v1 = Triangles[i].Index0;
        //        int v2 = Triangles[i].Index1;
        //        int v3 = Triangles[i].Index2;

        //        Vector3 ve1 = Vertices[v1].Vertex;
        //        Vector3 ve2 = Vertices[v2].Vertex;
        //        Vector3 ve3 = Vertices[v3].Vertex;

        //        Vector3 n1 = Vertices[v1].Normal;
        //        Vector3 n2 = Vertices[v2].Normal;
        //        Vector3 n3 = Vertices[v3].Normal;

        //        GL.glVertex3f(ve1.x, ve1.y, ve1.z);
        //        GL.glVertex3f(ve1.x + n1.x, ve1.y + n1.y, ve1.z + n1.z);

        //        GL.glVertex3f(ve2.x, ve2.y, ve2.z);
        //        GL.glVertex3f(ve2.x + n2.x, ve2.y + n2.y, ve2.z + n2.z);

        //        GL.glVertex3f(ve3.x, ve3.y, ve3.z);
        //        GL.glVertex3f(ve3.x + n3.x, ve3.y + n3.y, ve3.z + n3.z);
        //    }

        //    GL.glEnd();
        //}

        // Inspired by iq
        // http://www.iquilezles.org/www/articles/normals/normals.htm
        public void normalizeMesh()
        {
            
            //Vert* vert = myself->vert;
            //Triangle* face = myself->face;

            for (int i = 0; i < Vertices.Length; i++)
            {
                normalArray[i] = new Vector3(0.0f, 0.0f, 0.0f);
            }

            for (int i = 0; i < triangles.Length; i++)
            {
                int ia = triangles[i].Index0;
                int ib = triangles[i].Index1;
                int ic = triangles[i].Index2;

                Vector3 e1 = vertexArray[ia] - vertexArray[ib];
                Vector3 e2 = vertexArray[ic] - vertexArray[ib];
                Vector3 no = Vector3.CrossProduct(e1, e2);

                normalArray[ia] += no;
                normalArray[ib] += no;
                normalArray[ic] += no;
            }

            for (int i = 0; i < Vertices.Length; i++)
                normalArray[i].normalize(); ;
        }

    [StructLayout(LayoutKind.Sequential)]
    public struct ObjVertex
    {
        public Vector2 TexCoord;
        public Vector3 Normal;
        public Vector3 Vertex;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ObjTriangle
    {
        public int Index0;
        public int Index1;
        public int Index2;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ObjQuad
    {
        public int Index0;
        public int Index1;
        public int Index2;
        public int Index3;
    }
}
